package algo3.grupo3.controlador;

import java.util.Iterator;
import java.util.LinkedList;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;

import algo3.grupo3.modelo.Articulo;
import algo3.grupo3.modelo.ArticuloToleTole;
import algo3.grupo3.modelo.BloqueAcero;
import algo3.grupo3.modelo.BloqueConArticulo;
import algo3.grupo3.modelo.Bomba;
import algo3.grupo3.modelo.Bombita;
import algo3.grupo3.modelo.Chala;
import algo3.grupo3.modelo.Enemigo;
import algo3.grupo3.modelo.Salida;
import algo3.grupo3.modelo.Timer;
import algo3.grupo3.vista.EntidadArtToleTole;
import algo3.grupo3.vista.EntidadChala;
import algo3.grupo3.vista.EntidadBloqueAcero;
import algo3.grupo3.vista.EntidadBloqueConArticulo;
import algo3.grupo3.vista.EntidadBomba;
import algo3.grupo3.vista.EntidadBombita;
import algo3.grupo3.vista.EntidadEnemigo;
import algo3.grupo3.vista.EntidadSalida;
import algo3.grupo3.vista.EntidadTimer;
import algo3.grupo3.vista.VentanaJuego;

public class LogicaJuego {

	private static final int NIVEL_FINAL = 2;//originalmente 2 o 3
	private LinkedList<EntidadBomba> listaBombas= new LinkedList<EntidadBomba>();
	private LinkedList<EntidadEnemigo> listaEnemigos = new LinkedList<EntidadEnemigo>();
	private LinkedList<EntidadBloqueConArticulo> listaDeBloquesConArticulo = new LinkedList<EntidadBloqueConArticulo>();
	private LinkedList<EntidadBloqueAcero> listaDeBloquesAcero = new LinkedList<EntidadBloqueAcero>();
	private LinkedList<EntidadChala> listaDeChalas = new LinkedList<EntidadChala>();
	private LinkedList<EntidadTimer> listaDeTimers = new LinkedList<EntidadTimer>();
	private LinkedList<EntidadArtToleTole> listaDeToleToles = new LinkedList<EntidadArtToleTole>();
	private EntidadBombita entidadBombita;
	private EntidadSalida entidadSalida;
	
	//private final Thread T_ENEMIGOS = new Thread(new HiloActualizacionEnemigos());
	//private final Thread T_BOMBAS= new Thread(new HiloActualizacionBombas());
	private final Thread T_BOMBITA= new Thread(new HiloActualizacionBombita());
	private final Thread T_TIEMPO= new Thread(new HiloActualizacionTiempo());
	private final Thread T_BLOQUE = new Thread(new HiloActualizacionBloques());
	private final Thread T_ARTICULOS = new Thread(new HiloActualizacionArticulos());
	private final Thread T_BOMBAS_ENEMIGOS= new Thread(new HiloActualizacionBombasEnemigos());
	
	private  int delayTimer;
	private double tiempo= 0.0;	
	
	private ManejadorMemoria manipuladorMemoria;
	private VentanaJuego ventanaJuego;
	
	private int nivelActual= 1;
	private int puntos= 0;
	
	public LogicaJuego(){
		
		establecerDelayTimer(VentanaJuego.DELAY_TIMER);
		
		this.manipuladorMemoria= new ManejadorMemoria(this);
		
	}//fin de constructor	
	
	
	/**INICIO DE METODOS DE ACTUALIZACION-------------------------------------------------- */
	
	public synchronized void actualizar(){
		
		T_TIEMPO.run();
		T_BOMBAS_ENEMIGOS.run();
		T_BOMBITA.run();
		T_BLOQUE.run();
		T_ARTICULOS.run();
		
		
		
	}//fin de actualizar
	
	public synchronized  void actualizarTiempo(){
		this.tiempo+= delayTimer;
		if(tiempo > 10000)
			this.tiempo= 0.0;
	}//fin de actualizarTimer
	
	
	public synchronized void actualizarBloques(){
		this.actualizarBloquesDeAcero();
		
		if(this.listaDeBloquesConArticulo.isEmpty()){
			return;
		}
		
		Iterator<EntidadBloqueConArticulo> iteradorBca = listaDeBloquesConArticulo.iterator();
		EntidadBloqueConArticulo ebca;
		
		while(iteradorBca.hasNext()){
			ebca = iteradorBca.next();
			if(!ebca.objetoSigueActivo()){
				BloqueConArticulo bca = (BloqueConArticulo)ebca.obtenerObjeto();
				if(bca.tieneArticulo()){
					switch (bca.obtenerArticulo().obtenerTipoDeArticulo()) {
					case Articulo.CHALA:
						this.agregarChala((Chala)bca.obtenerArticulo());
						break;
					case Articulo.TIMER:
						this.agregarTimer((Timer)bca.obtenerArticulo());
						break;
					case Articulo.TOLETOLE:
						this.agregarArtToleTole((ArticuloToleTole)bca.obtenerArticulo());
						break;
					case Articulo.SALIDA:
						this.agregarSalida((Salida)bca.obtenerArticulo());
						break;
					}
				}
				iteradorBca.remove();
			}else{
				ebca.actualizar(delayTimer);
			}
		}
		
		iteradorBca = null;
		
		System.gc();
		
	}//fin de actualizarBloques
	
	private void actualizarBloquesDeAcero() {
		if(this.listaDeBloquesAcero.isEmpty()){
			return;
		}
		
		Iterator<EntidadBloqueAcero> iteradorBa = listaDeBloquesAcero.iterator();
		EntidadBloqueAcero eba;
		
		while(iteradorBa.hasNext()){
			eba = iteradorBa.next();
			if(!eba.objetoSigueActivo()){
				iteradorBa.remove();
			}else{
				eba.actualizar(delayTimer);
			}
		}
		
		iteradorBa = null;
		System.gc();
	}//fin de actualizarBloquesDeAcero
	
	
	private void actualizarChalas(){
		if(this.listaDeChalas.isEmpty()){
			return;
		}
		
		Iterator<EntidadChala> iteradorChala = listaDeChalas.iterator();
		EntidadChala entChala;
		
		while(iteradorChala.hasNext()){
			entChala = iteradorChala.next();
			if(!entChala.objetoSigueActivo()){
				iteradorChala.remove();
			}else{
				entChala.actualizar(delayTimer);
			}
		}
		iteradorChala = null;
		System.gc();
	}
	
	private void actualizarTimers(){
		if(this.listaDeTimers.isEmpty()){
			return;
		}
		
		Iterator<EntidadTimer> iteradorTimer = listaDeTimers.iterator();
		EntidadTimer entTimer;
		
		while(iteradorTimer.hasNext()){
			entTimer = iteradorTimer.next();
			if(!entTimer.objetoSigueActivo()){
				iteradorTimer.remove();
			}else{
				entTimer.actualizar(delayTimer);
			}
		}
		iteradorTimer = null;
		System.gc();
	}
	
	private void actualizarToleToles(){
		if(this.listaDeToleToles.isEmpty()){
			return;
		}
		
		Iterator<EntidadArtToleTole> iteradorToleTole = listaDeToleToles.iterator();
		EntidadArtToleTole entToleTole;
		
		while(iteradorToleTole.hasNext()){
			entToleTole = iteradorToleTole.next();
			if(!entToleTole.objetoSigueActivo()){
				iteradorToleTole.remove();
			}else{
				entToleTole.actualizar(delayTimer);
			}
		}
		iteradorToleTole = null;
		System.gc();
	}
	
	private void actualizarSalida(){
		if(this.entidadSalida == null){
			return;
		}
		
		if(this.entidadSalida.obtenerSalidaDelNivel().pasoDeNivel() == true){
			System.out.println("es posible pasar de nivel");
			this.ventanaJuego.pausarJuego();
			if(this.nivelActual == NIVEL_FINAL){
				
				this.ventanaJuego.ganarJuego();
				
			}else{
				System.out.println(nivelActual);
				int siguienteNivel= nivelActual+1;
				this.cargarNivel(siguienteNivel++);
			}
			return;
		}
		
		if(!this.entidadSalida.objetoSigueActivo()){
			this.entidadSalida = null;
		}else{
			this.entidadSalida.actualizar(delayTimer);
		}
		
		System.gc();
	}
	
	public void actualizarArticulos(){
		actualizarChalas();
		actualizarTimers();
		actualizarToleToles();
		actualizarSalida();
	}//fin de actualizarArticulos

	public synchronized void actualizarBombas(){
		
		if(this.listaBombas.isEmpty())
			return;
		Iterator<EntidadBomba> iterador = listaBombas.iterator();
		EntidadBomba eb;
		
		while(iterador.hasNext()){
			eb = iterador.next();
			if(!eb.objetoSigueActivo())
				iterador.remove();
			else
				eb.actualizar(delayTimer);
			/*falta agregar metodo que muestre una explosion de alguna manera al desaparecer una bomba */
		}
		
		iterador= null;
		System.gc();
		
	}//fin de actualizarBombas
	
	
	public synchronized void actualizarBombita(){
		if(this.entidadBombita==null)
			return;
		
		if(!entidadBombita.objetoSigueActivo()){
			this.entidadBombita= null;
			this.ventanaJuego.terminarJuego();
			return;
		}
		
		entidadBombita.actualizar(delayTimer);		
		
		System.gc();
		
	}//fin de actualizarBombita
	
	
	public synchronized void actualizarEnemigos(){
		
		if(this.listaEnemigos.isEmpty())
			return;
		Iterator<EntidadEnemigo> iterador = listaEnemigos.iterator();
		EntidadEnemigo ee;
		while(iterador.hasNext()){
			ee= iterador.next();
			if(!ee.objetoSigueActivo()){
				iterador.remove();
				this.sumarPunto();
			}
			else
				ee.actualizar(delayTimer);
		}
		
		iterador= null;
		System.gc();//llamo al recolector de basura
		
	}//fin de actualizarEnemigos
	

	
	public synchronized void agregarBomba(Bomba b){
		if(b== null)
			return;
		EntidadBomba eb= new EntidadBomba(b);
		this.listaBombas.add(eb);
	}//fin de agregarBomba
	
	
	public synchronized void agregarJugador(EntidadBombita eb){
		if(eb== null)
			return;
		this.entidadBombita= eb;
		
		if(this.ventanaJuego== null)
			return;
		
		ControlTecladoJugador ctj = this.ventanaJuego.obtenerControlTecladoJugador();
		ctj.cargarBombita(entidadBombita);
		//ctj.setearVentanaJuego(this.ventanaJuego);
		//ventanaJuego.establecerControlTecladoJugador(ctj);
		//this.ventanaJuego.addKeyListener(ctj);
	}//fin de agregarJugador
	
	public synchronized void agregarJugador(Bombita b){
		if(b==null)
			return;
		this.entidadBombita= new EntidadBombita(b);
		
		if(this.ventanaJuego== null)
			return;
		
		ControlTecladoJugador ctj = this.ventanaJuego.obtenerControlTecladoJugador();
		ctj.cargarBombita(entidadBombita);
		//ctj.setearVentanaJuego(this.ventanaJuego);
		//ventanaJuego.establecerControlTecladoJugador(ctj);
		//this.ventanaJuego.addKeyListener(ctj);		
		
				
	}//fin de agregarJugador*/
	
	public synchronized void agregarBloqueConArticulo(BloqueConArticulo bca){
		if(bca == null){
			return;
		}
		EntidadBloqueConArticulo ebca = new EntidadBloqueConArticulo(bca);
		this.listaDeBloquesConArticulo.add(ebca);
	}//fin de agregarBloqueConArticulo
	
	public synchronized void agregarBloqueAcero(BloqueAcero ba){
		if(ba == null){
			return;
		}
		EntidadBloqueAcero eba = new EntidadBloqueAcero(ba);
		this.listaDeBloquesAcero.add(eba);
	}//fin de agregarBloqueAcero
	
	public synchronized void agregarEnemigo(Enemigo e){
		if(e == null)
			return;
		EntidadEnemigo ee= new EntidadEnemigo(e);
		this.listaEnemigos.add(ee);
	}//fin de agregarEnemigo
	
	public synchronized void agregarChala(Chala unaChala){
		if(unaChala == null){
			return;
		}
		EntidadChala entChala = new EntidadChala(unaChala);
		this.listaDeChalas.add(entChala);
	}//fin de agregarChala
	
	public synchronized void agregarTimer(Timer unTimer){
		if(unTimer == null){
	    	return;
		}
		EntidadTimer entTimer = new EntidadTimer(unTimer);
		this.listaDeTimers.add(entTimer);
	}//fin de agregarTimer
	
	public synchronized void agregarArtToleTole(ArticuloToleTole unaToleTole){
		if(unaToleTole == null){
			return;
		}
		EntidadArtToleTole entTole = new EntidadArtToleTole(unaToleTole);
		this.listaDeToleToles.add(entTole);
	}
	
	public synchronized void agregarSalida(Salida unaSalida){
			if(unaSalida == null)
				return;
			this.entidadSalida = new EntidadSalida(unaSalida);
		}//fin de agregarSalida*/
	
	/**INICIO DE METODOS DE OBTENCION DE DATOS --------------------------------------------------------*/
	
	public EntidadBombita obtenerEntidadBombita(){
		return this.entidadBombita;
	}//fin de obtenerEntidadBombita
	
	public LinkedList<EntidadBomba> obtenerListaBombas(){
		return this.listaBombas;
	}//fin de obtenerListaBombas
	
	public LinkedList<EntidadBloqueConArticulo> obtenerListaBloquesConArticulo(){
		return this.listaDeBloquesConArticulo;
	}//fin de obtenerListaBloquesConArticulo
	
	public LinkedList<EntidadBloqueAcero> obtenerListaBloquesAcero(){
		return this.listaDeBloquesAcero;
	}//fin de obtenerListaBloquesAcero
	
	public LinkedList<EntidadEnemigo> obtenerListaEnemigos(){
		return this.listaEnemigos;
	}//fin de obtenerListaEnemigos
	
	public LinkedList<EntidadChala> obtenerListaChalas(){
		return this.listaDeChalas;
	}//fin de obtenerListaChalas
	
	public LinkedList<EntidadTimer> obtenerListaTimers(){
		return this.listaDeTimers;
	}//fin de obtenerListaTimers
	
	public LinkedList<EntidadArtToleTole> obtenerListaToleTole(){
		return this.listaDeToleToles;
	}//fin de obtenerListaToleTole
	
	
	public void vaciarListaBloquesAcero(){
		if(this.listaDeBloquesAcero.isEmpty())
			return;
		this.listaDeBloquesAcero.removeAll(listaDeBloquesAcero);
		
	}//fin de vaciarListaBloquesAcero
	
	public void vaciarListaBloquesConArticulo(){
		if(this.listaDeBloquesConArticulo.isEmpty())
			return;
		this.listaDeBloquesConArticulo.removeAll(listaDeBloquesConArticulo);
	}//fin de vaciarListaBloquesConArticulo
	
	public void vaciarListaEnemigos(){
		if(this.listaEnemigos.isEmpty())
			return;
		this.listaEnemigos.removeAll(listaEnemigos);
	}//fin de vaciarListaEnemigos
	
	public void vaciarListaBombas(){
		if(this.listaBombas.isEmpty())
			return;
		this.listaBombas.removeAll(listaBombas);
	}//fin de vaciarListaBombas
	
	
	public void removerBombita(){
		this.entidadBombita= null;
	}//fin de removerBombita
	
	public void vaciarListaChalas(){
		if(this.listaDeChalas.isEmpty())
			return;
		this.listaDeChalas.removeAll(listaDeChalas);
	}
	
	public void vaciarListaTimers(){
		if(this.listaDeTimers.isEmpty())
			return;
		this.listaDeTimers.removeAll(listaDeTimers);
	}
	
	public void vaciarListaToleToles(){
		if(this.listaDeToleToles.isEmpty())
			return;
		this.listaDeToleToles.removeAll(listaDeToleToles);
	}
	
	public void removerSalida(){
		this.entidadSalida= null;
	}
	
	
	public EntidadSalida obtenerSalida(){
		return this.entidadSalida;
	}//fin de obtenerListaEnemigos
	
	public double obtenerTiempo(){
		return this.tiempo;
	}//fin de obtenerTiempo
	
	/**INICIO DE DEFINICIONES DE THREADS --------------------------------------------------------------*/
		
	/*private class HiloActualizacionEnemigos implements Runnable{
		@Override
		public void run() {
			actualizarEnemigos();
		}//fin de run		
	}//fin de hiloActualizacionEnemigos
	
	private class HiloActualizacionBombas implements Runnable{
		@Override
		public void run(){
			actualizarBombas();
		}//fin de run
	}//fin de hiloActualizacionBombas*/
	
	private class HiloActualizacionBombasEnemigos implements Runnable{
		@Override
		public void run(){
			actualizarBombas();
			actualizarEnemigos();
		}
	}//fin de HiloActualizacionBombasEnemigos
	
	private class HiloActualizacionBombita implements Runnable{
		@Override
		public void run(){
			actualizarBombita();
		}//fin de run
	}//fin de hiloActualizacionBombita
	
	private class HiloActualizacionArticulos implements Runnable{
		@Override
		public void run(){
			actualizarArticulos();
		}
	}
	
	private class HiloActualizacionTiempo implements Runnable{
		@Override
		public void run(){
			actualizarTiempo();
		}
	}//fin de HiloActualizacionTimer
	
	private class HiloActualizacionBloques implements Runnable{
		@Override
		public void run(){
			actualizarBloques();
		}
	}//fin de HiloActualizacionBloques
	
	public double obtenerDelayTimer(){
		return delayTimer;
	}//fin de obtenerDelayTimer
	
	
	
	public void guardarPartida(){
		
		String direccionArchivo= seleccionarArchivoCargar();
		System.out.println(direccionArchivo);
		if(direccionArchivo=="")
			return;
		this.manipuladorMemoria.guardarDatos(direccionArchivo);
		
		
	}
	
	public String seleccionarArchivoCargar(){
	
		JFileChooser jfc= new JFileChooser("files//saved");
		jfc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
		int seleccion= jfc.showDialog(null, "ELEJIR_ARCHIVO");
		String pathArchivo= null;
		
		if(seleccion== JFileChooser.CANCEL_OPTION)
			return null;
		pathArchivo= jfc.getSelectedFile().getAbsolutePath();
		
		return pathArchivo;
		
	}//fin de seleccionarCarpetaCargar
	
	public void cargarPartida(){
		
		String direccionArchivo= this.seleccionarArchivoCargar();
		if(direccionArchivo== null)
			return;
		
		if(this.obtenerVentanaJuego().juegoEstaCorriendo())
			return;
		
		this.manipuladorMemoria.cargarDatos(direccionArchivo);
		this.ventanaJuego.iniciarJuego();
		
	}//fin de cargarPartida
	
	public void reiniciarJuego(){
		this.manipuladorMemoria.cargarDatos("files//stages//nivel1.xml");
		this.ventanaJuego.iniciarJuego();
		this.puntos= 0;
	}//fin de reiniciarJuego
	
	public void cargarNivel(int nivel){
		this.establecerNivelActual(nivel);
		this.reiniciarNivel();		
	}//fin de cargarNivel
	
	public void reiniciarNivel(){
		String pathArchivo= "files//stages//nivel";
		pathArchivo+=this.obtenerNivelActual();
		pathArchivo+=".xml";
		System.out.println(pathArchivo);
		this.manipuladorMemoria.cargarDatos(pathArchivo);
		this.ventanaJuego.iniciarJuego();
	}//fin de reiniciarNivel
	

	public void establecerVentanaJuego(VentanaJuego vj){
		if(vj== null)
			return;
		this.ventanaJuego= vj;
	}//fin de establecerVentanaJuego
	
	public VentanaJuego obtenerVentanaJuego(){
		return this.ventanaJuego;
	}//fin de obtenerVentanaJuego
	
	public void establecerDelayTimer(int dt){
		if(dt<=0)
			return;
		this.delayTimer= dt;
	}//fin de establecerDelayTimer
	
	public void establecerNivelActual(int n){
		if(n<0){
			this.nivelActual= 1;
			return;
		}
		this.nivelActual= n;
	}//fin de establecerNivelActual
	
	public int obtenerNivelActual(){
		return this.nivelActual;
	}//fin de obtenerNivelActual
	
	public synchronized void sumarPunto(){
		this.puntos++;
	}//fin de sumarPunto
	
	public void establecerPuntos(int p){
		if(p<0)
			return;
		this.puntos= p;
	}//fin de establecerPuntos
	
	public int obtenerPuntos(){
		return this.puntos;
	}//fin de obtenerPuntos
	
}//fin de LogicaJuego
